package com.gzz.orchid.common.webmvc.configuration;


import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.support.config.FastJsonConfig;
import com.alibaba.fastjson2.support.spring6.http.converter.FastJsonHttpMessageConverter;
import com.gzz.orchid.common.core.tuple.DateRange;
import com.gzz.orchid.common.core.tuple.DateTimeRange;
import com.gzz.orchid.common.webmvc.converter.enums.factory.IntegerToConverterFactory;
import com.gzz.orchid.common.webmvc.converter.enums.factory.StringToConverterFactory;
import com.gzz.orchid.common.webmvc.interceptor.IdempotentInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.context.request.async.TimeoutCallableProcessingInterceptor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * WebMvc 配置
 *
 * @author higmd
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    //@Value("${gzz.foceupload}")
    private String basePath = "D:/opt/uploads/";

    /**
     * 重写 addCorsMappings 方法，用于配置跨域资源共享（CORS）规则。
     * 跨域资源共享允许浏览器在跨源请求时，访问不同域名下的资源。
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 添加一个跨域映射规则，匹配所有以 /api/ 开头的请求路径，/** 表示匹配所有路径
        registry.addMapping("/**")
                // 允许所有头部信息，* 表示允许所有请求头
                .allowedHeaders("*")
                // 指定允许访问该资源的源（域名），这里*代表允许 所有域名的请求
                .allowedOriginPatterns("*")
                // 只允许来自特定前端域的请求
                //.allowedOrigins("http://your-frontend-domain.com")
                // 允许的HTTP方法,| * 允许所有请求方法
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                // 指定允许客户端访问的响应头，这里允许客户端访问 Authorization 和 Content-Disposition 这两个响应头
                .exposedHeaders("Authorization", "Content-Disposition")
                // 允许请求携带凭证（如 cookies、HTTP 认证等），设置为 true 表示允许
                .allowCredentials(true)
                // 设置预检请求（OPTIONS 请求）的缓存时间，单位为秒，这里设置为 3600 秒（即 1 小时），
                // 表示在 1 小时内，浏览器可以使用缓存的预检请求结果，而不需要再次发送预检请求
                .maxAge(3600);
    }

    /**
     * 静态文件资源
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /*
         * 资源映射路径
         * ( 必须在拦截器排除 )
         */
        //访问映射路径
        registry.addResourceHandler("/uploads/**")
                //资源绝对路径
                .addResourceLocations("file:" + basePath);
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(0);

    }

    /**
     * 配置拦截器, 注册到SpringMVC的拦截器链中
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 多语言拦截器
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        // 设置切换语言的请求参数名
        lci.setParamName("lang");
        registry.addInterceptor(lci);
        //  接口幂等性拦截器
        registry.addInterceptor(new IdempotentInterceptor())
                .excludePathPatterns("/uploads/**"
                        , "/**/*.js"
                        , "/**/*.css"
                        , "/**/*.html"
                        , "/**/*.png"
                        , "/**/*.ico"
                        , "/**/*.jpg"
                        , "/**/*.jpeg"
                        , "/**/*.gif"
                        , "/**/*.svg"
                        , "/**/*.ttf"
                        , "/**/*.woff"
                        , "/**/*.woff2"
                        , "/**/*.eot"
                        , "/**/*.map"
                        , "/**/*.ico"
                        , "/**/*.txt"
                        , "/**/*.pdf"
                );
    }

    /**
     * 转换器工厂 addConverterFactory
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        //  枚举类的转换器工厂
        registry.addConverterFactory(new IntegerToConverterFactory());
        registry.addConverterFactory(new StringToConverterFactory());
        //  日期转换器工厂
        registry.addConverter(new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(String source) {
                return LocalDate.parse(source, DATE_FORMATTER);
            }
        });
        registry.addConverter(new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                return LocalDateTime.parse(source, DATE_TIME_FORMATTER);
            }
        });
        // 日期范围转换器工厂
        registry.addConverter(new Converter<String[], DateRange>() {
            @Override
            public DateRange convert(String[] source) {
                if (source.length != 2) {
                    return null;
                } else {
                    try {
                        return new DateRange(LocalDate.parse(source[0], DATE_FORMATTER), LocalDate.parse(source[1], DATE_FORMATTER));
                    } catch (Exception e) {
                        return null;
                    }
                }
            }
        });
        registry.addConverter(new Converter<String[], DateTimeRange>() {
            @Override
            public DateTimeRange convert(String[] source) {
                if (source.length != 2) {
                    return null;
                } else {
                    try {
                        return new DateTimeRange(LocalDateTime.parse(source[0], DATE_TIME_FORMATTER), LocalDateTime.parse(source[1], DATE_TIME_FORMATTER));
                    } catch (Exception e) {
                        return null;
                    }
                }
            }
        });
        // 注册 JDK8 日期格式化器
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        registrar.registerFormatters(registry);
        // 注册传统日期格式化器
        DateFormatterRegistrar registrar2 = new DateFormatterRegistrar();
        registrar2.setFormatter(new DateFormatter("yyyy-MM-dd"));
        registrar2.registerFormatters(registry);

    }

    /**
     * 设置接口超时时间处理方法
     */
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        // 处理callable超时
        configurer.setDefaultTimeout(60 * 1000);
        configurer.setTaskExecutor(threadPoolTaskExecutor());
        configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
    }

    /**
     * 信息转换
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.setReaderFeatures(JSONReader.Feature.FieldBased, JSONReader.Feature.SupportArrayToBean);
        config.setWriterFeatures(JSONWriter.Feature.WriteMapNullValue, JSONWriter.Feature.PrettyFormat);
        converter.setFastJsonConfig(config);
        converter.setDefaultCharset(StandardCharsets.UTF_8);
    }

    /**
     * 请求参数验证
     *
     * @return
     */
    @Override
    public Validator getValidator() {
        return localValidatorFactoryBean();
    }

    /**
     * 配置线程池
     */
    @Bean(name = "asyncPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        // ThreadPoolTaskExecutor 这个类是sring为我们提供的线程池类
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        taskExecutor.setCorePoolSize(10);
        // 设置最大线程数
        taskExecutor.setMaxPoolSize(50);
        // 设置队列容量
        taskExecutor.setQueueCapacity(100);
        // 设置线程活跃时间（秒）
        taskExecutor.setKeepAliveSeconds(200);
        // 设置默认线程名称
        taskExecutor.setThreadNamePrefix("Async-");
        // 等待所有任务结束后再关闭线程池
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        // 设置拒绝策略
        // 线程池对拒绝任务（无线程可用）的处理策略，目前只支持AbortPolicy、CallerRunsPolicy；默认为后者
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }

    /**
     * 定时任务线程池
     */
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        // 设置线程池大小
        scheduler.setPoolSize(5);
        // 设置线程名称前缀
        scheduler.setThreadNamePrefix("task-scheduler-");
        // 等待任务在关机时完成
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 设置关机时等待任务完成的最大时长（毫秒）
        scheduler.setAwaitTerminationSeconds(60);
        return scheduler;
    }


    /**
     * 超时拦截器
     */
    @Bean
    public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
        return new TimeoutCallableProcessingInterceptor();
    }

    /**
     * 404 统一处理
     */
    @Bean
    @ConditionalOnMissingBean(value = BasicErrorController.class, search = SearchStrategy.CURRENT)
    public BasicErrorController basicErrorController(ErrorAttributes attributes, ServerProperties properties) {
        return new BasicErrorController(attributes, properties.getError()) {
            @Override
            public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
                HttpStatus status = getStatus(request);
                Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
                Map<String, Object> finalBody = new HashMap<>();
                finalBody.put("message", body.get("error").toString() + "<br>" + body.getOrDefault("path", ""));
                finalBody.put("code", status.value());
                //finalBody.put("data", List.of(body.getOrDefault("path", "")));
                return ResponseEntity.ok(finalBody);
            }
        };
    }

    /**
     * 多语言
     */
    //    public MessageSourceProperties messageSourceProperties() {
    //        return new MessageSourceProperties();
    //    }

    /**
     * 多语言资源文件
     */
    public MessageSource resourceMessageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        // 设置资源文件的基础名称
        //messageSource.setBasename("i18n/messages");
        messageSource.setBasenames("i18n/messages");
        //messageSource.addBasenames("i18n/errors");
        // 设置编码
        messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
        // 加载国际化资源后的过期时间，不设置不过期，单位秒
        //messageSource.setCacheSeconds(3600);
        // 是否始终使用MessageFormat格式化国际化消息，即使没有国际化消息参数，默认false
        messageSource.setAlwaysUseMessageFormat(false);
        // 找不到code时,是否直接返回code值，而不是抛异常，默认false，抛异常
        messageSource.setUseCodeAsDefaultMessage(false);
        // 找不到对应区域的语言时，是否回退到系统区域的语言，默认 true
        messageSource.setFallbackToSystemLocale(true);

        return messageSource;
    }

    public ReloadableResourceBundleMessageSource messageSource(MessageSourceProperties properties) {
        ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
        source.setBasenames("messages");
        source.setBasename("i18n/messages");
        source.setDefaultEncoding(StandardCharsets.UTF_8.toString());
        Duration cacheDu = properties.getCacheDuration();
        if (cacheDu != null) {
            source.setCacheMillis(cacheDu.toMillis());
        }
        return source;
    }

    /**
     * LocaleResolver接口来确定用 户的国际化区域
     */
    public LocaleResolver localeResolver() {
        // 核心实现类
        //        CookieLocaleResolver 持久化用户语言偏好（跨会话）
        //        AcceptHeaderLocaleResolver 默认策略，无需配置，但用户无法主动切换语言
        //        SessionLocaleResolver 会话内持久化，会话失效后丢失
        //        FixedLocaleResolver	强制固定区域，不可切换
        AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
        // 默认语言设置为美国英语
        resolver.setDefaultLocale(Locale.US);
        resolver.setSupportedLocales(Arrays.asList(Locale.US, Locale.CHINA));
        return resolver;
    }

    /**
     * 验证器
     */
    public LocalValidatorFactoryBean localValidatorFactoryBean() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(resourceMessageSource());
        return bean;
    }


    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        processor.setValidator(localValidatorFactoryBean().getValidator());
        return processor;
    }

}